/***************************************************************************
 *   Copyright (C) 2005 by xihe   *
 *   xihels@163.com                                                        *
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 *   This program is distributed in the hope that it will be useful,       *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
 *   GNU General Public License for more details.                          *
 *                                                                         *
 *   You should have received a copy of the GNU General Public License     *
 *   along with this program; if not, write to the                         *
 *   Free Software Foundation, Inc.,                                       *
 *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
 ***************************************************************************/
#ifndef _RECEIVE_IM_H_
#define _RECEIVE_IM_H_

#include <memory>
using std::auto_ptr;
#include "Packet.h"
#include "ReceiveIMHeader.h"
#include "ReceiveIMBody.h"


/**
 * 别人发来的消息包，如果是普通消息，格式为
 * 1. 头部
 * 2. 发送者QQ号，4字节
 * 3. 接收者QQ号，4字节
 * 4. 包序号（并非我们发送时候的序号，因为这个是4字节，可能是服务器端得总序号）
 * 5. 发送者IP，如果是服务器转发的，那么ip就是服务器ip， 4字节
 * 6. 发送者端口，如果是服务器转发的，那么就是服务器的端口，2字节
 * 7. 消息类型，是好友发的，还是陌生人发的，还是系统消息等等， 2字节
 * 8. 发送者QQ版本，2字节
 * 9. 发送者的QQ号，4字节
 * 10. 接受者的QQ号，4字节
 * 11. md5处理的发送方的uid和session key，16字节
 * 12. 普通消息类型，比如是文本消息还是其他什么消息，2字节
 * 13. 会话ID，2字节，如果是一个操作需要发送多个包才能完成，则这个id必须一致
 * 14. 发送时间，4字节
 * 15. 1字节未知
 * 16. 发送者头像，1字节
 * 17. 是否有字体属性，4字节，有一般是0x00000001
 * 18. 消息的分片数，1字节
 * 19. 分片序号，1字节，从0开始
 * 20. 消息id，2字节，同一条消息的不同分片id相同
 * 21. 消息类型，这里的类型表示是正常回复还是自动回复之类的信息， 1字节
 * 22. 消息正文，0是分隔符
 * 23. 字体属性，和SendIMPacket中的相同
 * 24. 尾部
 * 
 * 如果是系统消息：
 * 1-7. 与普通消息相同，只不过7是0x0030，表示是系统消息
 * 8. 系统消息类型，1字节
 * 9. 系统消息长度，1字节
 * 10. 系统消息
 * 
 * 如果是来自绑定手机的手机短消息，格式为：
 * 1-7. 与普通消息相同，只不过7是0x000B，表示手机短消息
 * 8.   未知1字节，0x0
 * 9.   发送者QQ号，4字节
 * 10.  未知1字节，0x0
 * 11.  未知1字节
 * 12.  发送者名称，最多13字节，不足后面补0
 * 13.  未知的1字节，0x4D
 * 14.  消息内容，一直到结尾都是，这个包长度200字节，如果不足，后面补0
 * 15.  尾部
 * 
 * 如果是来自移动QQ用户的手机短消息，格式为：
 * 1 - 13. 与绑定手机短信相同，只不过7为0x0013
 * 14. 短信发送时间
 * 15. 未知1字节，0x03
 * 16. 短信内容，0结尾，最大160字节
 * 17. 尾部
 * 
 * 如果是请求传送文件，格式为
 * 1 - 18. 与普通消息格式相同，差别只有12部分，为0x0035，表示是请求传送文件
 * 19.     未用部分，全0，15字节
 * 20.     传输类型，1字节
 * 21.     连接方式，1字节
 * 22.     请求者外部ip，4字节
 * 23.     请求者QQ端口，2字节，如果连接方式为0x3，则这个部分没有
 * 24.     第一个监听端口，2字节
 * 25.     请求者真实ip，4字节
 * 26.     第二个监听端口，2字节
 * 27.     空格符0x20
 * 28.     分隔符0x1F
 * 29.     文件名，不定长，不包含路径名
 * 30.     分隔符0x1F
 * 31.     文件字节数的字符串形式加“ 字节”
 * 32.     尾部
 * 
 * 如果是接受传送文件的请求，格式为
 * 1 - 18. 与普通消息格式相同，差别只有12部分，对于UDP请求，为0x0037
 * 19 - 26 和0x0035时相同
 * 27.     尾部 
* 
* 如果是通知文件传送的端口，格式为
* 1 - 18. 与普通消息格式相同，差别只有12部分，为0x003B，表示是通知文件传送端口
* 19 - 26 和0x0035时相同
* 27. 尾部
*
* 未知类型的群消息
* 1-7. 与普通消息相同，只不过发送者QQ号相当于是群的内部ID,7部分为0x0020
* 8.   群外部ID，4字节
* 9.   群类型，1字节
* 10.  发送者QQ号，4字节
* 11.  未知的两字节，全0
* 12.  消息序号，2字节
* 13.  消息发送时间，4字节
* 14.  未知的4字节
* 15.  后面的数据的长度，2字节
* 16.  以0结尾的消息内容
* 17.  字体属性，和SendIMPacket中的相同
* 18.  尾部
*  
* 如果是讨论组或者多人对话消息，讨论组和多人对话属于临时群，格式为
* 1 - 7 部分与普通消息相同，只不过7部分是0x002A
* 8.   父群内部ID，4字节
* 9.   群类型，1字节
* 10.  讨论组内部ID，4字节
* 11.  发送者QQ号，4字节
* 12.  未知的两字节，全0
* 13.  消息序号，2字节
* 14.  消息发送时间，4字节
* 15.  Version ID, 4字节，所谓version id，是我这里根据这个字段的意思乱编的。
*      其作用主要是标识群信息的版本，
*      比如一开始，群内有两个人，这个时候版本是0，
*      然后我删除一个人，那么群的版本就要加1，于是就变成了1。
*      然后我又加了2个人，于是版本再加1变成3，
*      假如我再删一个人又加一个人，那么版本就要加2变成5了。
*      为什么要加2呢，因为我做了两次操作，
*      一次删，一次加，所以版本号加了2，
*      你可能奇怪：为什么我不一次搞定，然后版本加1啊？因为
*      QQ的协议就是这样，它的协议不能同时做删除和添加成员的操作，晕吧？
*      所以我要做两次操作，
*      也就是要发两个包才能又加人又删人，所以版本号加了2。
*      那么这个字段有什么用呢？主要是用来
*      通知客户端群内的成员变化了以便刷新，
*      客户端对每一个群都要保存这么一个version ID，
*      一旦收到的消息大于我本地的version id，
*      说明群内成员变了，于是就发个包过去请求得到目前的成员列表。
*      所以你会看到，QQ在发临时群消息的时候，
*      成员如果变化了，会即时的反映出来，就是如此实现的了。
*      这个id不光是在修改成员的时候才变，修改基本信息也照样变，
*      用来表示这个群的信息修改过了。
*      注意：这个version id是从0开始持续增长的,
    *      所以如果是固定群，这个versionid，这个值可能会很大，
    *      因为它是固定的嘛，成员总是在变化，版本也就一直往上长了。
    * 16.  后面内容的长度
    * 17.  Content Type, 2字节，0x0001表示纯文件，0x0002表示有自定义表情
    * 18.  消息的分片数，1字节
    * 19.  分片序号，1字节，从0开始
    * 20.  消息id，2字节，同一条消息的不同分片id相同
    * 21.  4字节，未知
    * 22.  以0结尾的消息内容
    * 23.  字体属性，和SendIMPacket中的相同
    * 24.  尾部
    * 
    * 如果是示范群或会员创建的群发来的消息，这些群都是固定群，格式为
    * 1 - 7 部分与普通消息相同，只不过7部分是0x002B
    * 8.   群外部ID，4字节
    * 9.   群类型，1字节
    * 10.  发送者QQ号，4字节
    * 11.  未知的两字节
    * 12.  消息序号，2字节
    * 13.  消息发送时间，4字节
    * 14.  Version ID, 4字节
    * 15.  后面内容的长度
    * 16.  Content Type, 2字节，0x0001表示纯文件，0x0002表示有自定义表情
    * 17.  消息的分片数，1字节
    * 18.  分片序号，1字节，从0开始
    * 19.  消息id，2字节，同一条消息的不同分片id相同
    * 20.  4字节，未知
    * 21.  以0结尾的消息内容
    * 22.  字体属性，和SendIMPacket中的相同
    * 23.  尾部
    * 
    * 如果是0x0041，可能是向服务器报告最后连接情况的包，格式为
    * 1 - 27. 和0x003B时相同，差别只有命令不同，为0x0041
    * 
    * 如果是取消传送文件请求，格式为：
    * 1 - 18. 与普通消息格式相同，差别只有12部分，为0x0049，表示是取消传送文件
    * 19.     未用部分，全0，15字节
    * 20.     固定字节0x65
    * 21.     尾部
    */

    typedef auto_ptr<NormalIMHeader>	    NormalIMHeaderPtr;
    typedef auto_ptr<NormalIMBody>	    NormalIMBodyPtr;
    typedef auto_ptr<ClusterIMBody>	    ClusterIMBodyPtr;

    class ReceiveIMPacket : public InPacket{
	public:
	    ReceiveIMPacket(const uint8_t* buf, int len);

	    uint32_t getSender() const		{ return imHeader.getSender(); }
	    uint32_t getReceiver() const	{ return imHeader.getReceiver(); }
	    uint32_t getServerSequence() const  { return imHeader.getServerSequence(); }
	    uint32_t getSenderIp() const	{ return imHeader.getSenderIp(); }
	    uint16_t getSenderPort() const	{ return imHeader.getSenderPort(); }
	    uint16_t getMessageSource() const   { return imHeader.getMessageSource(); }

	    const NormalIMHeader* getNormalIMHeader() const { return normalIMHeaderPtr.get(); }
	    const NormalIMBody*   getNormalIMBody()   const { return normalIMBodyPtr.get(); }
	    const ClusterIMBody*  getClusterIMBody()  const { return clusterIMBodyPtr.get(); }	

	    const uint8_t*	  reply() const		    { return reply_; }
	private:
	    int parseNormalIM(const uint8_t* buf, int len);
	    int parseClusterIM(const uint8_t* buf, int len);

	private:
	    // 向服务器发送同步的数据
	    uint8_t reply_[16 + QQ_HEADER_LENGTH_BASIC_FAMILY_IN];
	    
	    ReceiveIMHeader	imHeader;		// 整个包的头

	    NormalIMHeaderPtr   normalIMHeaderPtr;	// 仅用于普通消息时
	    NormalIMBodyPtr	normalIMBodyPtr;

	    ClusterIMBodyPtr    clusterIMBodyPtr;	// 仅用于群消息时

    };
#endif // _RECEIVE_IM_H_

